/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 overlay_adv_object_image.cpp

	$Header: /heroes4/overlay_adv_object_image.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "overlay_adv_object_image.h"

#include "adv_object_image_visitor.h"

// ------------------------------------------------------------------------------
// Unnamed namespace
// ------------------------------------------------------------------------------

namespace
{
	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	int const k_current_format_version = 2;

} // Unnamed namespace

// ------------------------------------------------------------------------------
// t_overlay_adv_object_subimage class members
// ------------------------------------------------------------------------------

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_overlay_adv_object_subimage::draw_to(
	int						frame_num,
	t_screen_rect const &	source_rect,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_point,
	int						alpha ) const
{
	get_image().draw_column_to( get_column(), frame_num, source_rect, dest, dest_point );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_overlay_adv_object_subimage::draw_to(
	int						frame_num,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_point ) const
{
	get_image().draw_column_to( get_column(), frame_num, dest, dest_point );
}

// ------------------------------------------------------------------------------
// t_overlay_adv_object_image_base_base members
// ------------------------------------------------------------------------------

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_overlay_adv_object_image_base_base::t_overlay_adv_object_image_base_base(
	t_screen_rect const &	rect )
{
	int left_col = get_left_col( rect.left );
	int right_col = get_right_col( rect.right );
	m_column_depth_offset_deque.resize( right_col - left_col, 0 );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
int t_overlay_adv_object_image_base_base::get_column_depth_offset( int col ) const
{
	col -= get_left_column();
	if ( col < 0 )
		col = 0;
	else if ( col >= m_column_depth_offset_deque.size() )
		col = m_column_depth_offset_deque.size() - 1;
	return m_column_depth_offset_deque[ col ];
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_overlay_adv_object_image_base_base::set_column_depth_offset(
	int	col,
	int	new_depth_offset )
{
	col -= get_left_column();
	assert( col >= 0 && col < m_column_depth_offset_deque.size() );
	m_column_depth_offset_deque[ col ] = new_depth_offset;
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_screen_rect t_overlay_adv_object_image_base_base::get_column_rect(
	int	column_num ) const
{
	t_screen_rect image_rect = get_image_rect();
	t_screen_rect column_rect;
	column_rect.top = image_rect.top;
	column_rect.bottom = image_rect.bottom;
	column_rect.left = column_num * ( k_adventure_tile_width / 2 );
	column_rect.right = column_rect.left + k_adventure_tile_width / 2;
	return intersection( image_rect, column_rect );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
bool t_overlay_adv_object_image_base_base::read_version(
	std::streambuf &		stream,
	int						version,
	t_screen_rect const &	old_rect )
{
	try
	{
		std::deque< int >::size_type deque_size = get< t_uint32 >( stream );
		std::deque< int > new_column_depth_offset_deque( deque_size );
		std::deque< int >::iterator offset_iter = new_column_depth_offset_deque.begin();
		for ( ; offset_iter != new_column_depth_offset_deque.end(); ++offset_iter )
			*offset_iter = get< t_int16 >( stream );

		// If this is an old format version where the shadow was included in
		// extent of the image, trim any excess of the edges
		if ( version < 2 )
		{
			int old_left_col = get_left_col( old_rect.left );
			int old_right_col = get_right_col( old_rect.right );
			if ( new_column_depth_offset_deque.size() != old_right_col - old_left_col )
				return false;

			t_screen_rect new_rect = get_image_rect();
			int new_left_col = get_left_col( new_rect.left );
			int new_right_col = get_right_col( new_rect.right );

			if ( new_right_col < old_right_col )
			{
				new_column_depth_offset_deque.erase(
					new_column_depth_offset_deque.end() - ( old_right_col - new_right_col ),
					new_column_depth_offset_deque.end() );
			}

			if ( new_left_col > old_left_col )
			{
				new_column_depth_offset_deque.erase(
					new_column_depth_offset_deque.begin(),
					new_column_depth_offset_deque.begin() + ( new_left_col - old_left_col ) );
			}
		}

		if ( new_column_depth_offset_deque.size() != get_right_column() - get_left_column() )
			return false;

		m_column_depth_offset_deque.swap( new_column_depth_offset_deque );
	}
	catch ( t_streambuf_read_failure const & )
	{
		return false;
	}
	return true;
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
bool t_overlay_adv_object_image_base_base::write( std::streambuf & stream ) const
{
	try
	{
		put< t_uint32 >( stream, m_column_depth_offset_deque.size() );
		std::deque< int >::const_iterator offset_iter = m_column_depth_offset_deque.begin();
		for ( ; offset_iter != m_column_depth_offset_deque.end(); ++offset_iter )
			put< t_int16 >( stream, *offset_iter );
	}
	catch ( t_streambuf_write_failure const & )
	{
		return false;
	}
	return true;
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_overlay_adv_object_image_base_base::on_rect_changed(
	t_screen_rect const &	old_rect,
	t_screen_rect const &	new_rect )
{
	int old_left_col = get_left_col( old_rect.left );
	int old_right_col = get_right_col( old_rect.right );
	int new_left_col = get_left_col( new_rect.left );
	int new_right_col = get_right_col( new_rect.right );

	if ( old_left_col >= new_right_col || new_left_col >= old_right_col )
	{
		// The old and new ranges do not overlap
		m_column_depth_offset_deque.resize( new_right_col - new_left_col, 0 );
		std::fill( m_column_depth_offset_deque.begin(), m_column_depth_offset_deque.end(), 0 );
	}
	else
	{
		// The old and new ranges do overlap

		if ( new_left_col < old_left_col )
		{
			m_column_depth_offset_deque.insert(
				m_column_depth_offset_deque.begin(),
				old_left_col - new_left_col,
				0 );
		}
		else if ( new_left_col > old_left_col )
		{
			m_column_depth_offset_deque.erase(
				m_column_depth_offset_deque.begin(),
				m_column_depth_offset_deque.begin() + new_left_col - old_left_col );
		}

		if ( new_right_col > old_right_col )
		{
			m_column_depth_offset_deque.insert(
				m_column_depth_offset_deque.end(),
				new_right_col - old_right_col,
				0 );
		}
		else if ( new_right_col < old_right_col )
		{
			m_column_depth_offset_deque.erase(
				m_column_depth_offset_deque.end() - ( old_right_col - new_right_col ),
				m_column_depth_offset_deque.end() );
		}
	}
}

// ------------------------------------------------------------------------------
// t_overlay_adv_object_image_24 class members
// ------------------------------------------------------------------------------

#pragma warning( push )
#pragma warning( disable: 4355 )
// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_overlay_adv_object_image_24::t_overlay_adv_object_image_24()
	:	t_overlay_adv_object_image_base<
			t_simple_adv_object_image_24,
			t_overlay_adv_object_image_24,
			t_overlay_adv_object_subimage_24 >( *this )
{
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_overlay_adv_object_image_24::t_overlay_adv_object_image_24(
	t_overlay_adv_object_image_24 const &	other )
	:	t_overlay_adv_object_image_base<
			t_simple_adv_object_image_24,
			t_overlay_adv_object_image_24,
			t_overlay_adv_object_subimage_24 >( other, *this )
{
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_overlay_adv_object_image_24::t_overlay_adv_object_image_24(
	t_simple_adv_object_image_24 const &	other )
	:	t_overlay_adv_object_image_base<
			t_simple_adv_object_image_24,
			t_overlay_adv_object_image_24,
			t_overlay_adv_object_subimage_24 >( other, *this )
{
}
#pragma warning( pop )

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_overlay_adv_object_image_24::accept( t_visitor & visitor )
{
	visitor.visit( *this );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_overlay_adv_object_image_24::accept( t_visitor & visitor ) const
{
	visitor.visit( *this );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_overlay_adv_object_image_24::t_clone_ptr
t_overlay_adv_object_image_24::clone() const
{
	return t_clone_ptr( new t_overlay_adv_object_image_24( *this ) );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_overlay_adv_object_image_24::t_clone_16_ptr
t_overlay_adv_object_image_24::clone_as_16() const
{
	return t_clone_16_ptr( new t_overlay_adv_object_image( *this ) );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
bool t_overlay_adv_object_image_24::read( std::streambuf & stream )
{
	int version;
	try
	{
		version = get< t_uint16 >( stream );
	}
	catch ( t_streambuf_read_failure const & )
	{
		return false;
	}

	return read_version( stream, version );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
bool t_overlay_adv_object_image_24::read_version(
	std::streambuf &	stream,
	int					version )
{
	if ( version < 0 || version > k_current_format_version )
		return false;

	bool result;
	if ( version >= 1 )
		result = t_simple_adv_object_image_24::read( stream );
	else
		result = t_simple_adv_object_image_24::read_version( stream, 0 );

	return		result
			&&	t_overlay_adv_object_image_base_base::read_version(
					stream,
					version,
					get_extent( get_rect(), get_shadow_rect() ) );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
bool t_overlay_adv_object_image_24::write( std::streambuf & stream ) const
{
	try
	{
		put< t_uint16 >( stream, k_current_format_version );
	}
	catch ( t_streambuf_write_failure const & )
	{
		return false;
	}

	return		t_simple_adv_object_image_24::write( stream )
			&&	t_overlay_adv_object_image_base_base::write( stream );
}

// ------------------------------------------------------------------------------
// t_overlay_adv_object_image class members
// ------------------------------------------------------------------------------

#pragma warning( push )
#pragma warning( disable: 4355 )
// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_overlay_adv_object_image::t_overlay_adv_object_image()
	:	t_overlay_adv_object_image_base<
			t_simple_adv_object_image,
			t_overlay_adv_object_image,
			t_overlay_adv_object_subimage >( *this )
{
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_overlay_adv_object_image::t_overlay_adv_object_image(
	t_overlay_adv_object_image const &	other )
	:	t_overlay_adv_object_image_base<
			t_simple_adv_object_image,
			t_overlay_adv_object_image,
			t_overlay_adv_object_subimage >( other, *this )
{
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_overlay_adv_object_image::t_overlay_adv_object_image(
	t_overlay_adv_object_image_24 const & other )
	:	t_overlay_adv_object_image_base<
			t_simple_adv_object_image,
			t_overlay_adv_object_image,
			t_overlay_adv_object_subimage >( other, *this )
{
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_overlay_adv_object_image::t_overlay_adv_object_image(
	t_simple_adv_object_image const &	other )
	:	t_overlay_adv_object_image_base<
			t_simple_adv_object_image,
			t_overlay_adv_object_image,
			t_overlay_adv_object_subimage >( other, *this )
{
}
#pragma warning( pop )

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_overlay_adv_object_image::accept( t_visitor & visitor )
{
	visitor.visit( *this );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_overlay_adv_object_image::accept( t_visitor & visitor ) const
{
	visitor.visit( *this );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_overlay_adv_object_image::t_clone_ptr
t_overlay_adv_object_image::clone() const
{
	return t_clone_ptr( new t_overlay_adv_object_image( *this ) );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_overlay_adv_object_image::draw_column_to(
	int						column,
	int						frame_num,
	t_screen_rect const &	source_rect,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_point,
	int						alpha ) const
{
	assert( column >= get_left_column() && column < get_right_column() );

	t_screen_rect column_rect = get_rect();
	column_rect.left = column * ( k_adventure_tile_width / 2 );
	column_rect.right = column_rect.left + ( k_adventure_tile_width / 2 );
	if ( intersect( column_rect, source_rect ) )
		draw_to( frame_num, intersection( column_rect, source_rect ), dest, dest_point, alpha );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_overlay_adv_object_image::draw_column_to(
	int						column,
	int						frame_num,
	t_abstract_bitmap16 &	dest,
	t_screen_point const &	dest_point ) const
{
	assert( column >= get_left_column() && column < get_right_column() );

	t_screen_rect column_rect = get_rect();
	column_rect.left = column * ( k_adventure_tile_width / 2 );
	column_rect.right = column_rect.left + ( k_adventure_tile_width / 2 );
	draw_to( frame_num, column_rect, dest, dest_point );
}
